using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;

namespace LibGreen.Jabber.Managers
{
    /// <summary>
    /// A handler for events signalling the completion of reading the roster
    /// </summary>
    /// <param name="sender">The <see cref="RosterManager"/> raising the event</param>
    /// <param name="e">An empty <see cref="EventArgs"/></param>
    public delegate void RosterFinishedEventHandler(object sender, EventArgs e);

    /// <summary>
    /// Controls the client's roster, including adding, updating, and removing new items and groups
    /// </summary>
    public class RosterManager
    {
        /// <summary>
        /// Raised when the roster has arrived from the server
        /// </summary>
        public event RosterFinishedEventHandler RosterFinished;

        private readonly Session parentSession;
        private readonly List<RosterGroup> groups = new List<RosterGroup>();
        private readonly List<RosterItem> items = new List<RosterItem>();
        private readonly RosterGroup nullGroup = new RosterGroup("");

        /// <summary>
        /// Initializes a new RosterManager
        /// </summary>
        /// <param name="parentSession">The <see cref="Session"/> that owns this manager</param>
        public RosterManager(Session parentSession)
        {
            this.parentSession = parentSession;
        }

        /// <summary>
        /// Gets a list of <see cref="RosterGroup"/>s in the client's roster
        /// </summary>
        public ReadOnlyCollection<RosterGroup> Groups
        {
            get { return new ReadOnlyCollection<RosterGroup>(groups); }
        }

        /// <summary>
        /// Gets a list of <see cref="RosterItem"/>s that do not belong to a group
        /// </summary>
        public ReadOnlyCollection<RosterItem> UngroupedItems
        {
            get { return nullGroup.Items; }
        }

        #region Internal methods and properties
        /// <summary>
        /// Finds an existing <see cref="RosterGroup"/>, creating one if it doesn't already exist
        /// </summary>
        /// <param name="name">The name of the group to find</param>
        /// <returns>A <see cref="RosterGroup"/> with the indicated name</returns>
        internal RosterGroup FindGroup(string name)
        {
            lock (groups)
            {
                foreach (RosterGroup group in groups)
                {
                    if (group.Name == name)
                    {
                        return group;
                    }
                }

                RosterGroup newGroup = new RosterGroup(name);
                groups.Add(newGroup);
                return newGroup;
            }
        }

        /// <summary>
        /// Finds an existing <see cref="RosterItem"/>, creating one if it doesn't already exist
        /// </summary>
        /// <param name="jid">The <see cref="Jid"/> of the item to find</param>
        /// <returns>A <see cref="RosterItem"/> with the indicated JID</returns>
        internal RosterItem FindItem(Jid jid)
        {
            lock (items)
            {
                foreach (RosterItem item in items)
                {
                    if (item.JID == jid)
                    {
                        return item;
                    }
                }

                RosterItem newItem = new RosterItem(jid);
                items.Add(newItem);
                return newItem;
            }
        }

        /// <summary>
        /// Raises the <see cref="RosterFinished"/> event
        /// </summary>
        internal void OnRosterFinished()
        {
            if (RosterFinished != null)
            {
                RosterFinished(this, new EventArgs());
            }
        }

        /// <summary>
        /// Gets a <see cref="RosterGroup"/> used to collect <see cref="RosterItem"/>s that belong to no named groups
        /// </summary>
        internal RosterGroup NullGroup
        {
            get { return nullGroup; }
        }
        #endregion
    }
}
